package service

import (
	"boy-go/modules/system/model"
	"boy-go/modules/system/repository"
	"boy-go/modules/system/vo"
	"boy-go/pkg/constants"
	"boy-go/pkg/domain"
	"boy-go/pkg/request"
	"boy-go/pkg/xstr"
	"github.com/jinzhu/copier"
)

func NewSysMenuService() SysMenuService {
	return &sysMenuService{
		SysMenuRepo: repository.NewSysMenuRepository(),
		SysRoleRepo: repository.NewSysRoleRepository(),
	}
}

type sysMenuService struct {
	SysMenuRepo *repository.SysMenuRepository
	SysRoleRepo *repository.SysRoleRepository
}

// GetById 根据id获取配置信息
func (s *sysMenuService) GetById(menuId int64) (*vo.SysMenuModel, error) {
	m, err := s.SysMenuRepo.GetById(menuId)
	if err != nil {
		return nil, err
	}
	rsp := new(vo.SysMenuModel)
	if err := copier.Copy(rsp, m); err != nil {
		return nil, err
	}
	return rsp, nil
}

func (s *sysMenuService) List(req *vo.QuerySysMenuReq) (*vo.ListSysMenuRsp, error) {

	pageReq := &request.PageRequest{
		PageNum:  req.PageNum,
		PageSize: req.PageSize,
	}

	list, total, err := s.SysMenuRepo.List(req, pageReq)

	if err != nil {
		return nil, err
	}

	itemList := make([]*vo.SysMenuModel, 0)
	for _, v := range list {
		item := new(vo.SysMenuModel)
		if err := copier.Copy(item, &v); err != nil {
			continue
		}
		itemList = append(itemList, item)
	}
	return &vo.ListSysMenuRsp{
		List:  itemList,
		Total: total,
	}, nil
}

// Add 添加菜单权限表
func (s *sysMenuService) Add(req *vo.AddSysMenuReq) (*vo.AddSysMenuRsp, error) {
	m := new(model.SysMenu)
	err := copier.Copy(m, req)
	if err != nil {
		return nil, err
	}
	if err := s.SysMenuRepo.Save(m); err != nil {
		return nil, err
	}
	return &vo.AddSysMenuRsp{
		MenuId: m.MenuId,
	}, nil
}

// Edit 修改菜单权限表
func (s *sysMenuService) Edit(req *vo.EditSysMenuReq) (*vo.EditSysMenuRsp, error) {
	m := new(model.SysMenu)
	err := copier.Copy(m, req)
	if err != nil {
		return nil, err
	}
	if err := s.SysMenuRepo.Update(m); err != nil {
		return nil, err
	}
	return &vo.EditSysMenuRsp{
		MenuId: m.MenuId,
	}, nil
}

// Del 删除菜单权限表
func (s *sysMenuService) Del(menuId int64) error {
	return s.SysMenuRepo.Delete(menuId)
}

func (s *sysMenuService) SelectMenuListByUserId(req *vo.QuerySysMenuReq, userId int64) ([]*model.SysMenu, error) {
	if model.IsAdmin(&userId) {
		ms, _, err := s.SysMenuRepo.List(req, nil)
		if err != nil {
			return nil, err
		}
		return ms, nil
	} else {
		ms, err := s.SysMenuRepo.SelectMenuListByUserId(userId)
		if err != nil {
			return nil, err
		}
		return ms, nil
	}
}

func (s *sysMenuService) SelectMenuTreeByUserId(userId int64) ([]*model.SysMenu, error) {
	if model.IsAdmin(&userId) {
		ms, err := s.SysMenuRepo.SelectMenuTreeAll()
		if err != nil {
			return nil, err
		}
		return s.getChildPerms(ms, 0), nil
	} else {
		ms, err := s.SysMenuRepo.SelectMenuListByUserId(userId)
		if err != nil {
			return nil, err
		}
		return s.getChildPerms(ms, 0), nil
	}
}

func (s *sysMenuService) getChildPerms(list []*model.SysMenu, parentId int64) []*model.SysMenu {
	returnList := make([]*model.SysMenu, 0)
	for _, v := range list {
		if *v.ParentId == parentId {
			s.recursionFn(list, v)
			returnList = append(returnList, v)
		}
	}
	return returnList
}

// recursionFn 递归组装子菜单
func (s *sysMenuService) recursionFn(list []*model.SysMenu, t *model.SysMenu) {
	childList := s.getChildList(list, t)
	t.Children = childList
	for _, tChild := range childList {
		if s.hasChild(list, tChild) {
			s.recursionFn(list, tChild)
		}
	}
}

// getChildList 获取当前节点的所有直接子节点
func (s *sysMenuService) getChildList(list []*model.SysMenu, t *model.SysMenu) []*model.SysMenu {
	var tlist []*model.SysMenu
	for _, node := range list {
		if *node.ParentId == t.MenuId {
			tlist = append(tlist, node)
		}
	}
	return tlist
}

// hasChild 判断当前节点是否有子节点
func (s *sysMenuService) hasChild(list []*model.SysMenu, t *model.SysMenu) bool {
	if len(s.getChildList(list, t)) > 0 {
		return true
	}
	return false
}
func isMenuFrame(menu *model.SysMenu) bool {
	if menu != nil {
		if *menu.ParentId == 0 && *menu.MenuType == model.SysMenu_TYPE_MENU && *menu.IsFrame == "1" {
			return true
		}
	}
	return false
}
func getRouteName(menu *model.SysMenu) string {
	if isMenuFrame(menu) {
		return ""
	}
	return getRouteNameByMenuNameAndPath(menu.RouteName, menu.Path)
}
func getRouteNameByMenuNameAndPath(name *string, path *string) string {
	routerName := ""
	if xstr.IsNotEmpty(name) {
		routerName = *name
	} else {
		routerName = *path
	}
	return xstr.Capitalize(routerName)
}

// isInnerLink 是否为内链组件
func isInnerLink(menu *model.SysMenu) bool {
	if menu == nil {
		return false
	}
	return *menu.IsFrame == "1" && xstr.IsHttp(menu.Path)
}

// 内链域名特殊字符替换
func innerLinkReplaceEach(path *string) string {
	str, _ := xstr.ReplaceEach(*path, []string{constants.HTTP, constants.HTTPS, constants.WWW, ".", ":"}, []string{"", "", "", "/", "/"})
	return str
}
func isParentView(menu *model.SysMenu) bool {
	return *menu.ParentId != 0 && *menu.MenuType == model.SysMenu_TYPE_DIR
}
func getRouterPath(menu *model.SysMenu) string {
	if menu != nil {
		routerPath := *menu.Path
		//内链打开外网方式
		if *menu.ParentId != 0 && isInnerLink(menu) {
			routerPath = innerLinkReplaceEach(menu.Path)
		}
		if *menu.ParentId == 0 && model.SysMenu_TYPE_DIR == *menu.MenuType && *menu.IsFrame == "1" {
			routerPath = "/" + *menu.Path
		} else if isMenuFrame(menu) {
			routerPath = "/"
		}
		return routerPath
	}
	return ""
}
func getComponent(menu *model.SysMenu) string {
	component := "Layout"
	if xstr.IsNotEmpty(menu.Component) && !isMenuFrame(menu) {
		component = *menu.Component
	} else if xstr.IsEmpty(menu.Component) && *menu.ParentId != 0 && isInnerLink(menu) {
		component = "InnerLink"
	} else if xstr.IsEmpty(menu.Component) && isParentView(menu) {
		component = "ParentView"
	}
	return component
}

// BuildMenus 构建前端路由所需要的菜单
func (s *sysMenuService) BuildMenus(menus []*model.SysMenu) []*domain.RouterVo {
	var routers []*domain.RouterVo
	for _, menu := range menus {
		router := domain.RouterVo{
			Hidden:    (menu.Visible != nil && *menu.Visible == "1"),
			Name:      getRouteName(menu),
			Path:      getRouterPath(menu),
			Component: getComponent(menu),
			Query:     menu.Query,
			Meta:      domain.BuildMetaVo(*menu.MenuName, menu.Icon, *menu.IsCache == "1", menu.Path),
		}
		cMenus := menu.Children
		if cMenus != nil && len(cMenus) > 0 && *menu.MenuType == model.SysMenu_TYPE_DIR {
			noRedirect := "noRedirect"
			alwaysShow := true
			router.AlwaysShow = &alwaysShow
			router.Redirect = &noRedirect
			router.Children = s.BuildMenus(cMenus)
		} else if isMenuFrame(menu) {
			router.Meta = nil
			childrenList := []*domain.RouterVo{
				{
					Path:      *menu.Path,
					Component: *menu.Component,
					Name:      getRouteNameByMenuNameAndPath(menu.RouteName, menu.Path),
					Meta:      domain.BuildMetaVo(*menu.MenuName, menu.Icon, *menu.IsCache == "1", menu.Path),
					Query:     menu.Query,
				},
			}
			router.Children = childrenList
		} else if *menu.ParentId == 0 && isInnerLink(menu) {
			router.Meta = &domain.MetaVo{
				Title: *menu.MenuName,
				Icon:  menu.Icon,
			}
			router.Path = "/"

			routerPath := innerLinkReplaceEach(menu.Path)
			childrenList := []*domain.RouterVo{
				{
					Path:      routerPath,
					Component: "InnerLink",
					Name:      getRouteNameByMenuNameAndPath(menu.RouteName, &routerPath),
					Meta: &domain.MetaVo{
						Title: *menu.MenuName,
						Icon:  menu.Icon,
						Link:  menu.Path,
					},
				},
			}
			router.Children = childrenList
		}

		routers = append(routers, &router)
	}

	return routers
}

func (s *sysMenuService) BuildMenuTree(menus []*model.SysMenu) []*model.SysMenu {
	var returnList []*model.SysMenu
	tempMap := make(map[int64]bool)
	// 提取所有 menuId 到 map 中，用于快速查找是否存在 parentId
	for _, menu := range menus {
		tempMap[menu.MenuId] = true
	}
	// 遍历所有菜单项，找出顶级节点（其 ParentId 不在任何 menuId 中）
	for i := range menus {
		menu := menus[i]
		if !tempMap[*menu.ParentId] {
			// 是顶级节点，递归构建子节点
			s.recursionFn(menus, menu)
			returnList = append(returnList, menu)
		}
	}
	// 如果没有找到顶级节点，则返回原始列表
	if len(returnList) == 0 {
		return menus
	}
	return returnList
}

func (s *sysMenuService) BuildMenuTreeSelect(menus []*model.SysMenu) []*domain.TreeSelect {
	menuTrees := s.BuildMenuTree(menus)
	var treeSelects []*domain.TreeSelect
	for i := range menuTrees {
		ts := s.convertToTreeSelect(menuTrees[i])
		treeSelects = append(treeSelects, ts)
	}
	return treeSelects
}

// convertToTreeSelect 将 SysMenu 转换为 TreeSelect（递归）
func (s *sysMenuService) convertToTreeSelect(menu *model.SysMenu) *domain.TreeSelect {
	ts := &domain.TreeSelect{
		ID:    menu.MenuId,
		Label: *menu.MenuName,
	}
	for _, child := range menu.Children {
		ts.Children = append(ts.Children, s.convertToTreeSelect(child))
	}
	return ts
}
func (s *sysMenuService) SelectMenuListByRoleId(roleId int64) []*int64 {
	role, err := s.SysRoleRepo.GetById(roleId)
	if err != nil {
		return nil
	}
	ids, err := s.SysMenuRepo.SelectMenuListByRoleId(roleId, role.MenuCheckStrictly != nil && *role.MenuCheckStrictly == true)
	if err != nil {
		return nil
	}
	return ids
}

// 根据角色ID查询权限
func (s *sysMenuService) SelectMenuPermsByUserId(userId int64) []*string {
	perms := s.SysMenuRepo.SelectMenuPermsByUserId(userId)
	if perms == nil {
		return nil
	}
	set := map[string]bool{}
	res := make([]*string, 0)
	for _, perm := range perms {
		if v, ok := set[*perm]; !ok || !v {
			res = append(res, perm)
			set[*perm] = true
		}
	}
	return res
}

// 根据角色ID查询权限
func (s *sysMenuService) SelectMenuPermsByRoleId(roleId int64) []*string {
	perms := s.SysMenuRepo.SelectMenuPermsByRoleId(roleId)
	if perms == nil {
		return nil
	}
	set := map[string]bool{}
	res := make([]*string, 0)
	for _, perm := range perms {
		if v, ok := set[*perm]; !ok || !v {
			res = append(res, perm)
			set[*perm] = true
		}
	}
	return res
}
